package bma.common.thrift.xdom;

import java.util.ArrayList;
import java.util.List;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TField;
import org.apache.thrift.protocol.TList;
import org.apache.thrift.protocol.TMap;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolUtil;
import org.apache.thrift.protocol.TSet;
import org.apache.thrift.protocol.TStruct;
import org.apache.thrift.protocol.TType;

import bma.common.langutil.xdom.Xdom;
import bma.common.langutil.xdom.core.XdomArray;
import bma.common.langutil.xdom.core.XdomBoolean;
import bma.common.langutil.xdom.core.XdomMap;
import bma.common.langutil.xdom.core.XdomNULL;
import bma.common.langutil.xdom.core.XdomNumber;
import bma.common.langutil.xdom.core.XdomString;

public class Reader {

	private DynamicThriftManager manager;

	public Reader(DynamicThriftManager manager) {
		super();
		this.manager = manager;
	}

	public DynamicThriftManager getManager() {
		return manager;
	}

	public void setManager(DynamicThriftManager manager) {
		this.manager = manager;
	}

	public Xdom readStruct(TDynamicStruct struct, TProtocol in)
			throws TException {
		XdomMap r = new XdomMap();
		TField schemeField;
		TStruct s = in.readStructBegin();
		if(struct==null) {
			struct = manager.getStruct(s.name);
		}
		while (true) {
			schemeField = in.readFieldBegin();
			if (schemeField.type == org.apache.thrift.protocol.TType.STOP) {
				break;
			}
			boolean readed = false;
			TDynamicField dfield = struct.getField(schemeField.id);
			if (dfield != null) {
				if (schemeField.type == dfield.getTypeValue()) {
					readed = true;
					Xdom v = read(dfield.getType(), in);
					if (v != null) {
						r.put(dfield.getName(), v);
					}
				}
			}
			if (!readed) {
				TProtocolUtil.skip(in, schemeField.type);
			}
			in.readFieldEnd();
		}
		in.readStructEnd();
		return r;
	}

	public Xdom read(TDynamicType type, TProtocol in) throws TException {
		switch (type.getType()) {
		case TType.VOID:
			return XdomNULL.NULL;
		case TType.BOOL:
			return new XdomBoolean(in.readBool());
		case TType.BYTE:
			return new XdomNumber(in.readByte());
		case TType.DOUBLE:
			return new XdomNumber(in.readDouble());
		case TType.I16:
			return new XdomNumber(in.readI16());
		case TType.I32:
			return new XdomNumber(in.readI32());
		case TType.I64:
			return new XdomNumber(in.readI64());
		case TType.STRING:
			return new XdomString(in.readString());
		case TType.STRUCT: {
			TDynamicStruct struct = manager.getStruct(type.getName());
			return readStruct(struct, in);
		}
		case TType.MAP: {
			TMap map = in.readMapBegin();
			List<Xdom> r = new ArrayList<Xdom>(2 * map.size);
			for (int i = 0; i < map.size; ++i) {
				Xdom key = read(type.getKeyType(), in);
				Xdom val = read(type.getInnerType(), in);
				XdomMap e = new XdomMap();
				e.put("key", key);
				e.put("value", val);
				r.add(e);
			}
			in.readMapEnd();
			return new XdomArray(r);
		}
		case TType.SET: {
			TSet set = in.readSetBegin();
			List<Xdom> r = new ArrayList(set.size);
			for (int i = 0; i < set.size; ++i) {
				Xdom v = read(type.getInnerType(), in);
				r.add(v);
			}
			in.readListEnd();
			return new XdomArray(r);
		}
		case TType.LIST: {
			TList list = in.readListBegin();
			List<Xdom> r = new ArrayList(list.size);
			for (int i = 0; i < list.size; ++i) {
				Xdom v = read(type.getInnerType(), in);
				r.add(v);
			}
			in.readListEnd();
			return new XdomArray(r);
		}
		case TType.ENUM:
			return new XdomNumber(in.readI32());
		}
		return XdomNULL.NULL;
	}

}
